package com.gloaba.gateway.filter;


import com.gloaba.common.core.constant.SecurityConstants;
import com.gloaba.common.core.utils.StringUtils;
import com.gloaba.gateway.config.properties.CustomGatewayProperties;
import com.gloaba.gateway.constant.RequestLogConstants;
import com.gloaba.gateway.service.IApiLogService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.Optional;

/**
 * 全局日志过滤器
 * <p>
 * 用于打印请求执行参数与响应时间等等
 *
 * @author Zane
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class GlobalLogFilter implements GlobalFilter, Ordered {

    private final CustomGatewayProperties customGatewayProperties;
    private final IApiLogService iApiLogService;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String clientType = Optional.ofNullable(request.getHeaders().getFirst(SecurityConstants.REQUEST_HEADER_CLIENT_TYPE)).orElse("PC");
        String appType = Optional.ofNullable(request.getHeaders().getFirst(SecurityConstants.REQUEST_HEADER_APP_TYPE)).orElse("ERP");


        iApiLogService.buildApiLog(exchange);

        if (!customGatewayProperties.getRequestLog()) {
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                iApiLogService.updateApiLog(exchange, null);
            }));
        }

        ServerHttpResponseDecorator response = recordResponseLog(exchange);
        return chain.filter(exchange.mutate().response(response).build()).then(Mono.fromRunnable(() -> {
            iApiLogService.updateApiLog(exchange, null);
        }));
    }

    /**
     * 记录响应日志
     * 通过 DataBufferFactory 解决响应体分段传输问题。
     */
    private ServerHttpResponseDecorator recordResponseLog(ServerWebExchange exchange) {
        ServerHttpResponse response = exchange.getResponse();
        DataBufferFactory bufferFactory = response.bufferFactory();
        return new ServerHttpResponseDecorator(response) {
            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                if (body instanceof Flux) {
                    //获取响应类型
                    String originalResponseContentType = exchange.getAttribute(ServerWebExchangeUtils.ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR);//
                    log.info("recordResponseLog originalResponseContentType =========== " + originalResponseContentType);
                    exchange.getAttributes().put(RequestLogConstants.API_RESULT_CONTENT_TYPE, originalResponseContentType);
                    String url = exchange.getRequest().getURI().getPath();
                    if (ObjectUtils.equals(this.getStatusCode(), HttpStatus.OK)//
                            && StringUtils.isNotBlank(originalResponseContentType)//
                            && originalResponseContentType.contains("application/json")) {
                        //只有返回成功，并且是json格式的数据，才记录结果
                        Flux<? extends DataBuffer> fluxBody = Flux.from(body);
                        try {
                            return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
                                // 合并多个流集合，解决返回体分段传输
                                DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
                                DataBuffer join = dataBufferFactory.join(dataBuffers);
                                byte[] content = new byte[join.readableByteCount()];
                                join.read(content);
                                // 释放掉内存
                                DataBufferUtils.release(join);
                                String responseResult = new String(content, StandardCharsets.UTF_8);
                                log.info("responseResult =========== " + responseResult);
                                //记录结果到属性里，带到下一个步，下面的过滤器也可以获取
                                exchange.getAttributes().put(RequestLogConstants.API_RESULT_LOG, responseResult);

                                //将结果再写回响应
                                return bufferFactory.wrap(content);
                            }));
                        } catch (Exception ex) {
                            log.error("recordResponseLog error========== ", ex);
                            return super.writeWith(fluxBody);
                        }
                    }
                }                // if body is not a flux. never got there.
                return super.writeWith(body);
            }
        };
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE + 99;
    }


}
